/*
 * Copyright (c) 2020, chao
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-08-19     chao         the first version
 */

#include <rtthread.h>
#include <rtdevice.h>
#include "xpt2046.h"
#include "drv_gpio.h"
#include "touch.h"

#ifdef BSP_USING_XPT2046 
 
#define DBG_TAG "xpt2046"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define XPT_CS_PIN          GET_PIN(B, 2)
#define XPT_CLK_PIN         GET_PIN(B, 1)
#define XPT_MOSI_PIN        GET_PIN(F, 9)
#define XPT_MISO_PIN        GET_PIN(F, 8)
#define XPT_PEN_PIN         GET_PIN(F, 10)

#define XPT2046_CS_0        rt_pin_write(XPT_CS_PIN, PIN_LOW)
#define XPT2046_CS_1        rt_pin_write(XPT_CS_PIN, PIN_HIGH)
#define XPT2046_CLK_0       rt_pin_write(XPT_CLK_PIN, PIN_LOW)
#define XPT2046_CLK_1       rt_pin_write(XPT_CLK_PIN, PIN_HIGH)
#define XPT2046_MOSI_0      rt_pin_write(XPT_MOSI_PIN, PIN_LOW)
#define XPT2046_MOSI_1      rt_pin_write(XPT_MOSI_PIN, PIN_HIGH)
#define XPT2046_MISO_STA    rt_pin_read(XPT_MISO_PIN)

#define CMD_READ_AD_X       (0xD0)
#define CMD_READ_AD_Y       (0x90)

static rt_mutex_t xpt_lock;

static void xpt2046_write_value(rt_uint8_t value)
{
    uint8_t i=0; 

    for (i=0; i<8; i++)
    {   
        XPT2046_CLK_0;
        if (value & 0x80)
        {
            XPT2046_MOSI_1;  
        }
        else 
        {
            XPT2046_MOSI_0;  
        }
        value <<= 1;    
        XPT2046_CLK_1;
        XPT2046_CLK_0;
    }
    XPT2046_MOSI_0;     
}
 
static rt_uint8_t xpt2046_read_value(void)
{
    uint8_t i=0, value=0; 

    for (i=0; i<8; i++)  
    {   
        value <<= 1;
        XPT2046_CLK_0;
        XPT2046_CLK_1;
        if (XPT2046_MISO_STA)
        {
            value++;       
        }
        XPT2046_CLK_0;
    }
    return value;
}

static uint16_t xpt2046_read_ad_value(uint8_t cmd)
{    
    uint8_t high=0, low=0; 

    XPT2046_CS_0;
    xpt2046_write_value(cmd);
    high = xpt2046_read_value()&0x7F;
    low  = xpt2046_read_value()&0xF8;
    XPT2046_CS_1;
    return (((high<<8)|low)>>3);   
}

static void xpt2046_init(void)
{
    rt_pin_mode(XPT_CS_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(XPT_CLK_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(XPT_MOSI_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(XPT_MISO_PIN, PIN_MODE_INPUT);
    rt_pin_mode(XPT_PEN_PIN, PIN_MODE_INPUT);
    xpt_lock = rt_mutex_create("xpt_lock", RT_IPC_FLAG_FIFO);
}
 
static rt_size_t xpt2046_readpoint(struct rt_touch_device *touch, void *data, rt_size_t touch_num)
{
    struct rt_touch_data *touch_data;
    rt_err_t result;

    touch_data = (struct rt_touch_data *)data;

    if (rt_pin_read(XPT_PEN_PIN)==0)
    {
        result = rt_mutex_take(xpt_lock, RT_WAITING_FOREVER);
        if (result == RT_EOK)
        {
            touch_data->event = RT_TOUCH_EVENT_DOWN;
            touch_data->x_coordinate = xpt2046_read_ad_value(CMD_READ_AD_X);
            touch_data->y_coordinate = xpt2046_read_ad_value(CMD_READ_AD_Y);
        }
        rt_mutex_release(xpt_lock);
    }
    else
    {
        if (touch_data->event == RT_TOUCH_EVENT_DOWN)
        {
            touch_data->event = RT_TOUCH_EVENT_UP;
        }
        else
        {
            touch_data->event = RT_TOUCH_EVENT_NONE;
            touch_data->x_coordinate = 0;
            touch_data->y_coordinate = 0;
        }
    }
    touch_data->timestamp = rt_tick_get();

    return RT_EOK;
}

static rt_err_t xpt2046_control(struct rt_touch_device *touch, int cmd, void *arg)
{
    struct rt_touch_info *info;

    switch(cmd)
    {
    case RT_TOUCH_CTRL_GET_ID:
        break;
    case RT_TOUCH_CTRL_GET_INFO:
        info = (struct rt_touch_info *)arg;
        info->type = touch->info.type;
        info->vendor = touch->info.vendor;
        info->point_num = touch->info.point_num;
        info->range_x = touch->info.range_x;
        info->range_y = touch->info.range_y;
        break;
    case RT_TOUCH_CTRL_SET_MODE:
        break;
    case RT_TOUCH_CTRL_SET_X_RANGE:
        break;
    case RT_TOUCH_CTRL_SET_Y_RANGE:
        break;
    case RT_TOUCH_CTRL_SET_X_TO_Y:
        break;
    case RT_TOUCH_CTRL_DISABLE_INT:
        break;
    case RT_TOUCH_CTRL_ENABLE_INT:
        break;
    default:
        break;
    }

    return RT_EOK;
}
 
 static const struct rt_touch_ops ops =
 {
     xpt2046_readpoint,
     xpt2046_control,
 };
 
 static struct rt_touch_info info =
 {
     RT_TOUCH_TYPE_RESISTANCE,
     RT_TOUCH_VENDOR_UNKNOWN,
     1,
     240,
     320,
 };
 
int rt_hw_xpt2046_init(const char *device_name)
{
    struct rt_touch_device *touch = RT_NULL;

    touch = (struct rt_touch_device *)rt_calloc(1, sizeof(struct rt_touch_device));
    if (touch == RT_NULL)
    {
        return -RT_ENOMEM;
    }
 
    xpt2046_init();

    touch->info = info;
    touch->ops = &ops;

    if (rt_hw_touch_register(touch, device_name, RT_DEVICE_FLAG_RDONLY, RT_NULL) != RT_EOK)
    {
        return -RT_EIO;
    }

    return RT_EOK;
}

#endif /* BSP_USING_XPT2046 */


